iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 11
0
Software Development

Android Architecture系列 第 11

Dependency Injection with Dagger2: Part 3

  • 分享至 

  • xImage
  •  

GithubBrowserSample中對於Dagger有一些進階的用法,包括自動inject到Activity/Fragment中,以及使用Module封裝ViewModelFactory讓往後新增ViewModel時不用再修改ViewModelFactory,這兩天就來將這兩種方式加入我們的程式中。

Automatically inject to Activity/Fragment

當我們要在Activity或Fragment使用DI的時候都要呼叫inject(this),由於每次都要重複這個動作,Google就寫了一個AppInjector搭配一些程式小修改來自動處理這件事。

首先必須標明Activity/Fragment是否有用到DI,建立一個interface Injectable:

/**
 * Marks an activity / fragment injectable.
 */
public interface Injectable {
}

因為只是單純的標示用途所以裡面不用任何method。

接著就在我們要用DI的Activity及Fragment加上Injectable:

public class MainActivity extends AppCompatActivity implements Injectable, 
        HasSupportFragmentInjector {

    ...
}
public class RepoFragment extends Fragment implements Injectable {

    ...
}

建立AppInjector:

/**
 * Helper class to automatically inject fragments if they implement {@link Injectable}.
 */
public class AppInjector {

    private AppInjector() {}

    public static void init(GithubApp githubApp) {

        DaggerAppComponent.builder()
                .application(githubApp)
                .build()
                .inject(githubApp);

        githubApp
                .registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityCreated(Activity activity, 
                                                  Bundle savedInstanceState) {
                        handleActivity(activity);
                    }

                    @Override
                    public void onActivityStarted(Activity activity) {

                    }

                    @Override
                    public void onActivityResumed(Activity activity) {

                    }

                    @Override
                    public void onActivityPaused(Activity activity) {

                    }

                    @Override
                    public void onActivityStopped(Activity activity) {

                    }

                    @Override
                    public void onActivitySaveInstanceState(Activity activity, 
                                                            Bundle outState) {

                    }

                    @Override
                    public void onActivityDestroyed(Activity activity) {

                    }
                });
    }

    private static void handleActivity(Activity activity) {
        if (activity instanceof Injectable || activity instanceof HasSupportFragmentInjector) {
            AndroidInjection.inject(activity);
        }
        if (activity instanceof FragmentActivity) {
            ((FragmentActivity) activity).getSupportFragmentManager()
                    .registerFragmentLifecycleCallbacks(
                            new FragmentManager.FragmentLifecycleCallbacks() {
                                @Override
                                public void onFragmentPreAttached(FragmentManager fm, 
                                                                  Fragment f,
                                                                  Context context) {
                                    if (f instanceof Injectable) {
                                        AndroidSupportInjection.inject(f);
                                    }
                                }
                            }, true);
        }
    }
}

init(GithubApp githubApp)中的DaggerAppComponent那段跟我們原本在GithubApp中的一樣,只是改成在這呼叫而已。

從Activity開始著手,我們用registerActivityLifecycleCallbacks註冊每一個Activity的生命週期callback,在onActivityCreated的時候呼叫handleActivity(activity),此method會檢查Activity是否為Injectable或HasSupportFragmentInjector,若是的話執行inject。

Fragment的部分,在handleActivity(activity)會檢查Activity是否為FragmentActivity,若是的話註冊其下Fragment的生命週期callback,並在onFragmentPreAttached檢查Fragment是否為Injectable以執行inject。

註1:google sample中只用HasSupportFragmentInjector判斷Activity是否有用到DI,這在該sample中沒有問題,但我們若有其他Activity是本身有使用DI但其下沒有Fragment或Fragment沒有使用DI的話將導致條件式不成立而沒有執行inject;我在Activity加上Injectable並在檢查式加上activity instanceof Injectable,讓只有Activity本身用到DI時也會自動inject。

註2:對於Fragment的處理,google sample是在onFragmentCreated中執行inject,我依照Dagger官方文件的說明改成在onFragmentPreAttached執行。

註3:一堆中英文夾雜看起來很亂,但其實內容不難且我盡力修飾了,給點機會。

最後在Application中初始化AppInjector就完成了:

public class GithubApp extends Application implements HasActivityInjector {

    ...
    
    @Override
    public void onCreate() {
        super.onCreate();
        ...
        AppInjector.init(this);
    }

    ...
}

接著就可以把Activity和Fragment中的inject(this)刪掉,只要implement Injectable就好囉。


GitHub source code:
https://github.com/IvanBean/ITBon2018/tree/day11-dagger-auto-inject


上一篇
Dependency Injection with Dagger2: Part 2
下一篇
Dependency Injection with Dagger2: Part 4
系列文
Android Architecture30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言